/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.threadsample;

import android.app.IntentService;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;

import org.apache.http.HttpStatus;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.Vector;

/**
 * This service pulls RSS content from a web site URL contained in the incoming Intent (see onHandleIntent()). As it runs, it broadcasts its
 * status using LocalBroadcastManager; any component that wants to see the status should implement a subclass of BroadcastReceiver and
 * register to receive broadcast Intents with category = CATEGORY_DEFAULT and action Constants.BROADCAST_ACTION.
 * 
 */
public class RSSPullService extends IntentService
{
	// Used to write to the system log from this class.
	public static final String	LOG_TAG			= "RSSPullService";

	// Defines and instantiates an object for handling status updates.
	private BroadcastNotifier	mBroadcaster	= new BroadcastNotifier(this);

	/**
	 * An IntentService must always have a constructor that calls the super constructor. The string supplied to the super constructor is
	 * used to give a name to the IntentService's background thread.
	 */
	public RSSPullService()
	{

		super("RSSPullService");
	}

	/**
	 * In an IntentService, onHandleIntent is run on a background thread. As it runs, it broadcasts its current status using the
	 * LocalBroadcastManager.
	 * 
	 * @param workIntent
	 *            The Intent that starts the IntentService. This Intent contains the URL of the web site from which the RSS parser gets
	 *            data.
	 */
	@Override
	protected void onHandleIntent(Intent workIntent)
	{
		// Gets a URL to read from the incoming Intent's "data" value
		String localUrlString = workIntent.getDataString();

		// Creates a projection to use in querying the modification date table in the provider.
		final String[] dateProjection = new String[] { DataProviderContract.ROW_ID, DataProviderContract.DATA_DATE_COLUMN };

		// A URL that's local to this method
		URL localURL;

		// A cursor that's local to this method.
		Cursor cursor = null;

		/*
		 * A block that tries to connect to the Picasa featured picture URL passed as the "data"
		 * value in the incoming Intent. The block throws exceptions (see the end of the block).
		 */
		try
		{

			// Convert the incoming data string to a URL.
			localURL = new URL(localUrlString);

			/*
			 * Tries to open a connection to the URL. If an IO error occurs, this throws an
			 * IOException
			 */
			URLConnection localURLConnection = localURL.openConnection();

			// If the connection is an HTTP connection, continue
			if ((localURLConnection instanceof HttpURLConnection))
			{

				// Broadcasts an Intent indicating that processing has started.
				mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);

				// Casts the connection to a HTTP connection
				HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;

				// Sets the user agent for this request.
				localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);

				/*
				 * Queries the content provider to see if this URL was read previously, and when.
				 * The content provider throws an exception if the URI is invalid.
				 */
				cursor = getContentResolver().query(DataProviderContract.DATE_TABLE_CONTENTURI, dateProjection, null, null, null);

				// Flag to indicate that new metadata was retrieved
				boolean newMetadataRetrieved;

				/*
				 * Tests to see if the table contains a modification date for the URL
				 */
				if (null != cursor && cursor.moveToFirst())
				{

					// Find the URL's last modified date in the content provider
					long storedModifiedDate = cursor.getLong(cursor.getColumnIndex(DataProviderContract.DATA_DATE_COLUMN));

					/*
					 * If the modified date isn't 0, sets another request property to ensure that
					 * data is only downloaded if it has changed since the last recorded
					 * modification date. Formats the date according to the RFC1123 format.
					 */
					if (0 != storedModifiedDate)
					{
						localHttpURLConnection.setRequestProperty("If-Modified-Since", org.apache.http.impl.cookie.DateUtils.formatDate(
								new Date(storedModifiedDate), org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
					}

					// Marks that new metadata does not need to be retrieved
					newMetadataRetrieved = false;

				}
				else
				{

					/*
					 * No modification date was found for the URL, so newmetadata has to be
					 * retrieved.
					 */
					newMetadataRetrieved = true;

				}

				// Reports that the service is about to connect to the RSS feed
				mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);

				// Gets a response code from the RSS server
				int responseCode = localHttpURLConnection.getResponseCode();

				switch (responseCode)
				{

				// If the response is OK
					case HttpStatus.SC_OK:

						// Gets the last modified data for the URL
						long lastModifiedDate = localHttpURLConnection.getLastModified();

						// Reports that the service is parsing
						mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);

						/*
						 * Instantiates a pull parser and uses it to parse XML from the RSS feed.
						 * The mBroadcaster argument send a broadcaster utility object to the
						 * parser.
						 */
						RSSPullParser localPicasaPullParser = new RSSPullParser();

						localPicasaPullParser.parseXml(localURLConnection.getInputStream(), mBroadcaster);

						// Reports that the service is now writing data to the content provider.
						mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);

						// Gets image data from the parser
						Vector<ContentValues> imageValues = localPicasaPullParser.getImages();

						// Stores the number of images
						int imageVectorSize = imageValues.size();

						// Creates one ContentValues for each image
						ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];

						imageValuesArray = imageValues.toArray(imageValuesArray);

						/*
						 * Stores the image data in the content provider. The content provider
						 * throws an exception if the URI is invalid.
						 */
						getContentResolver().bulkInsert(DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);

						// Creates another ContentValues for storing date information
						ContentValues dateValues = new ContentValues();

						// Adds the URL's last modified date to the ContentValues
						dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);

						if (newMetadataRetrieved)
						{

							// No previous metadata existed, so insert the data
							getContentResolver().insert(DataProviderContract.DATE_TABLE_CONTENTURI, dateValues);

						}
						else
						{

							// Previous metadata existed, so update it.
							getContentResolver().update(
									DataProviderContract.DATE_TABLE_CONTENTURI,
									dateValues,
									DataProviderContract.ROW_ID + "="
											+ cursor.getString(cursor.getColumnIndex(DataProviderContract.ROW_ID)), null);
						}
						break;

				}

				// Reports that the feed retrieval is complete.
				mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
			}

			// Handles possible exceptions
		}
		catch (MalformedURLException localMalformedURLException)
		{

			localMalformedURLException.printStackTrace();

		}
		catch (IOException localIOException)
		{

			localIOException.printStackTrace();

		}
		catch (XmlPullParserException localXmlPullParserException)
		{

			localXmlPullParserException.printStackTrace();

		}
		finally
		{

			// If an exception occurred, close the cursor to prevent memory leaks.
			if (null != cursor)
			{
				cursor.close();
			}
		}
	}

}
